#include "GnDX9RendererPCH.h"
#include "GnDX9Renderer.h"
#include "GnDX9SystemDesc.h"

//class test : public NiRefObject
//{
//public:
//	test(int i){ii =i;}
//	int ii;
//};
//char memore[256000] = {0, };
//DWORD time = ::GetTickCount();
//const unsigned int roof = 200000000;
//testPtr ss = NiNew test(2);
//for (unsigned int i = 0 ; i < roof; i++)
//{
//	testPtr gg = ss;
//}
//time = ::GetTickCount() - time ;
//sprintf( memore, "%d\n", time );
//OutputDebugString(memore);


static GnDX9RendererEBM GnDX9RendererEBMObject;

HINSTANCE GnDX9Renderer::mspD3D9Module = NULL;
Direct3D9Ptr GnDX9Renderer::mspD3D = NULL;

void GnDX9Renderer::EBMStartup()
{
}
void GnDX9Renderer::EBMShutdown()
{
	D3D9_POINTER_RELEASE(mspD3D);
	if( mspD3D9Module )
		FreeLibrary(mspD3D9Module);
	mspD3D9Module = NULL;
}

GnDX9Renderer::DX9CreationParameters::DX9CreationParameters( guint useFlags, guint width, guint height,
	GnWindowHandle deviceHandle, GnWindowHandle focusHandle, unsigned int adapterIndex,
	DeviceDesc desc, BackBufferFormat backBufferFormat, DepthStencilBuffer depthStencilFormat,
	SwapEffect swapEffect, MultiSampleType bfMode, guint backBufferCount, guint refreshRateInHz,
	guint presentationInterval ) : GnRenderer::CreationParameters( GnRenderer::RENDERERDIRECTX9, width,
	height, useFlags, deviceHandle, focusHandle ), mAdapterIndex(adapterIndex), mDeviceDesc(desc),
	mBackBufferFormat(backBufferFormat), mDepthStencilFormat(depthStencilFormat),
	mSwapEffect(swapEffect), mMultiSampleType(bfMode), mBackBufferCount(backBufferCount),
	mPresentationInterval(presentationInterval)
{
	mCreate = GnDX9Renderer::Create;
}

bool GnDX9Renderer::CreateD3D9()
{
	if( mspD3D )
		return true;

#if defined(GNDEBUG)
	mspD3D9Module = LoadLibrary("D3D9D.DLL");
	if (mspD3D9Module == NULL)
		mspD3D9Module = LoadLibrary("D3D9.DLL");
#else //#if defined(GNDEBUG)
	mspD3D9Module = LoadLibrary("D3D9.DLL");
#endif //#if defined(GNDEBUG)

	LPDIRECT3DCREATE9FUNC createFunc;
	if ( mspD3D9Module )
		createFunc = (LPDIRECT3DCREATE9FUNC)GetProcAddress(mspD3D9Module, "Direct3DCreate9");
	else
		return false;

	if( createFunc )
		mspD3D = (*createFunc)(D3D_SDK_VERSION);

	if( !mspD3D )
		return false;
	
	return true;
}

GnDX9Renderer::GnDX9Renderer(void) : mpDevice(NULL), mBehaviorFlags(NULL), mpSystemDesc(NULL),
	mpAdapterDesc(NULL), mpDeviceDesc(NULL), mDeviceType(D3DDEVTYPE_HAL)
{
	mpSystemDesc = GnNew GnDX9SystemDesc(mspD3D);
}

GnDX9Renderer::~GnDX9Renderer(void)
{
	GnDelete mpSystemDesc;
	D3D9_POINTER_RELEASE( mpDevice );
}
GnRenderer* GnDX9Renderer::Create(CreationParameters* createParam)
{
	if( !CreateD3D9() )
	{
		GnError( GnText("Error - Failed D3D9 Create" ) );
		return NULL;
	}
	GnAssert(createParam->mRendererType == GnRenderer::RENDERERDIRECTX9);
	return GnNew GnDX9Renderer();
}

bool GnDX9Renderer::Init(CreationParameters* createParam)
{
	DX9CreationParameters* dx9CreateParam = (DX9CreationParameters*)createParam;
	if ( !SelectDeviceType( dx9CreateParam->mDeviceDesc, mDeviceType, mBehaviorFlags ) )
	{
		GnError("Error : Creation failed: Invalid 3D device type\n NiDX9Renderer::Initialize\n");
		return false;
	}

	if( dx9CreateParam->mUseFlags & USE_FPU_PRESERVE )
		mBehaviorFlags |= D3DCREATE_FPU_PRESERVE;

	if( dx9CreateParam->mUseFlags & USE_MULTITHREADED )
		mBehaviorFlags |= D3DCREATE_MULTITHREADED;

	mpAdapterDesc = mpSystemDesc->GetAdapterDesc( dx9CreateParam->mAdapterIndex );
	mpDeviceDesc = mpAdapterDesc->GetDeviceDesc( mDeviceType );

	D3DPRESENT_PARAMETERS outD3DPresentParams;
	SetD3DPresetnParameters( dx9CreateParam, outD3DPresentParams );

	HRESULT hr = mspD3D->CreateDevice( dx9CreateParam->mAdapterIndex, mDeviceType,
		dx9CreateParam->mDeviceHandle, mBehaviorFlags, &outD3DPresentParams,
		&mpDevice );

	if( mpDevice == NULL )
	{
		GnError( GnText("Error - Failed D3D9 Create" ) );
		return false;
	}
	return true;
}

bool GnDX9Renderer::SetD3DPresetnParameters(DX9CreationParameters* createParam,
	D3DPRESENT_PARAMETERS& outD3DPresentParams)
{
	bool windowed = (createParam->mUseFlags & USE_FULLSCREEN) ? false : true;
	guint& width = createParam->mWidth;
	guint& height = createParam->mHeight;	
	guint& useFlags = createParam->mUseFlags;
	unsigned int& adapterIndex = createParam->mAdapterIndex;
	BackBufferFormat& gnBackBufferFormat = createParam->mBackBufferFormat;
	DepthStencilBuffer& gnDepthStencilFormat = createParam->mDepthStencilFormat;	
	D3DFORMAT backBufferFormat = GetD3DFormat(createParam->mBackBufferFormat);
	D3DFORMAT depthStencilFormat = GetD3DFormat(createParam->mDepthStencilFormat);
	unsigned int& backBufferCount = createParam->mBackBufferCount;
	SwapEffect& swapEffect = createParam->mSwapEffect;
	MultiSampleType& multiSampleType = createParam->mMultiSampleType;
	GnWindowHandle& deviceHandle = createParam->mDeviceHandle;
	guint& refreshRateInHz = createParam->mRefreshRateInHz;
	const D3DCAPS9& d3dCaps = mpDeviceDesc->GetCaps();
	guint& presentationInterval = createParam->mPresentationInterval;

	guint bitDepth = 32;
	if (useFlags & USE_16BITBUFFERS)
		bitDepth = 16;

	if( windowed == false && ( height < MINHEIGHT || width < MINWIDTH ) )
	{
		height = GnMax(height, MINHEIGHT);
		width = GnMax(height, MINWIDTH);
		GnWarring("Warring : Not Supported Window Size");
	}

	if( backBufferFormat == D3DFMT_UNKNOWN )
		backBufferFormat = mpDeviceDesc->GetNearestBackBufferFormat( windowed, bitDepth );
	else
		backBufferFormat = mpDeviceDesc->ValidBackBufferFormat( backBufferFormat, windowed, bitDepth );

	if (backBufferFormat == D3DFMT_UNKNOWN)
	{
		GnError(" Error : Not use user choice BackBufeerFormat");
		return false;
	}
	
	
	D3DFORMAT adapterFormat = GetCompatibleAdapterFormat( gnBackBufferFormat );
	if( depthStencilFormat == D3DFMT_UNKNOWN )
	{
		depthStencilFormat = mpDeviceDesc->GetNearestDepthStencilFormat( adapterFormat, backBufferFormat,
			windowed, bitDepth, (useFlags & USE_STENCIL) ? 8 : 0 );
	}
	else
	{
		depthStencilFormat = mpDeviceDesc->ValidDepthStencilFormat( depthStencilFormat, adapterFormat,
			backBufferFormat, windowed, bitDepth, (useFlags & USE_STENCIL) ? 8 : 0 );
	}
	
	//if( depthStencilFormat == D3DFMT_UNKNOWN )
	//{
	//	GnError(" Error : Not use user choice DepthStencilFormat");
	//	return false;
	//}


	D3DMULTISAMPLE_TYPE d3dMultiSampleType = D3DMULTISAMPLE_NONE;
	guint multiSampleQuilty = 0;
	GetNearestMultiSampleType( multiSampleType,useFlags, adapterFormat, backBufferFormat,
		windowed, d3dMultiSampleType, multiSampleQuilty );

	D3DSWAPEFFECT d3dSwapEffect = GetSwapEffect( swapEffect, d3dMultiSampleType );

	outD3DPresentParams.BackBufferWidth = width;
	outD3DPresentParams.BackBufferHeight = height;
	outD3DPresentParams.BackBufferFormat = backBufferFormat;
	outD3DPresentParams.BackBufferCount = GetBackBufferCount( d3dSwapEffect, backBufferCount );	

	outD3DPresentParams.MultiSampleType = d3dMultiSampleType;
	outD3DPresentParams.MultiSampleQuality = multiSampleQuilty;

	outD3DPresentParams.SwapEffect = d3dSwapEffect;
	outD3DPresentParams.Windowed = windowed;
	outD3DPresentParams.hDeviceWindow = deviceHandle;


	outD3DPresentParams.EnableAutoDepthStencil = (useFlags & USE_AUTODEPTHSTENCIL) ? true : false;
	outD3DPresentParams.AutoDepthStencilFormat = depthStencilFormat;
	
	if( useFlags & USE_LOCKBLE_BACKBUFFER )
		outD3DPresentParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	else
		outD3DPresentParams.Flags = 0;

	if( windowed )
		refreshRateInHz = 0;
	else
		refreshRateInHz = GetRefreshRate( adapterFormat, width, height, refreshRateInHz );

	outD3DPresentParams.FullScreen_RefreshRateInHz = refreshRateInHz;

	if( windowed )
	{
		if( presentationInterval == D3DPRESENT_INTERVAL_TWO ||
			presentationInterval == D3DPRESENT_INTERVAL_THREE ||
			presentationInterval == D3DPRESENT_INTERVAL_FOUR )
		{
			presentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		}
	}
	else if( !( d3dCaps.PresentationIntervals & presentationInterval ) )
	{
		outD3DPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	}
	outD3DPresentParams.PresentationInterval = presentationInterval;
	return true;
}

guint GnDX9Renderer::GetRefreshRate( D3DFORMAT adapterFormat, guint width, guint height,
	guint& refreshRate )
{
	switch(refreshRate)
	{
	case REFRESHRATE_DEFAULT:
		return D3DPRESENT_RATE_DEFAULT;
	case REFRESHRATE_BEST:		
		if( mpAdapterDesc->ValidateFullscreenMode( adapterFormat, width, height, refreshRate ) != NULL )
			return refreshRate;
	}
	return D3DPRESENT_RATE_DEFAULT;
}

void GnDX9Renderer::GetNearestMultiSampleType( MultiSampleType multiSampleType, guint flags, 
	D3DFORMAT adapterFromat, D3DFORMAT backBufferFormat, bool bWindowed,
	D3DMULTISAMPLE_TYPE& outD3DMultiSample, guint& outMultiSampleQuilty )
{
	switch (multiSampleType)
	{
	case MSTYPE_DEFAULT:
	case MSTYPE_MULTISAMPLES_NONE:
		outD3DMultiSample = D3DMULTISAMPLE_NONE;
	case MSTYPE_MULTISAMPLES_NONMASKABLE:
		outD3DMultiSample = D3DMULTISAMPLE_NONMASKABLE;
	case MSTYPE_MULTISAMPLES_2:
		outD3DMultiSample = D3DMULTISAMPLE_2_SAMPLES;
	case MSTYPE_MULTISAMPLES_3:
		outD3DMultiSample = D3DMULTISAMPLE_3_SAMPLES;
	case MSTYPE_MULTISAMPLES_4:
		outD3DMultiSample = D3DMULTISAMPLE_4_SAMPLES;
	case MSTYPE_MULTISAMPLES_5:
		outD3DMultiSample = D3DMULTISAMPLE_5_SAMPLES;
	case MSTYPE_MULTISAMPLES_6:
		outD3DMultiSample = D3DMULTISAMPLE_6_SAMPLES;
	case MSTYPE_MULTISAMPLES_7:
		outD3DMultiSample = D3DMULTISAMPLE_7_SAMPLES;
	case MSTYPE_MULTISAMPLES_8:
		outD3DMultiSample = D3DMULTISAMPLE_8_SAMPLES;
	case MSTYPE_MULTISAMPLES_9:
		outD3DMultiSample = D3DMULTISAMPLE_9_SAMPLES;
	case MSTYPE_MULTISAMPLES_10:
		outD3DMultiSample = D3DMULTISAMPLE_10_SAMPLES;
	case MSTYPE_MULTISAMPLES_11:
		outD3DMultiSample = D3DMULTISAMPLE_11_SAMPLES;
	case MSTYPE_MULTISAMPLES_12:
		outD3DMultiSample = D3DMULTISAMPLE_12_SAMPLES;
	case MSTYPE_MULTISAMPLES_13:
		outD3DMultiSample = D3DMULTISAMPLE_13_SAMPLES;
	case MSTYPE_MULTISAMPLES_14:
		outD3DMultiSample = D3DMULTISAMPLE_14_SAMPLES;
	case MSTYPE_MULTISAMPLES_15:
		outD3DMultiSample = D3DMULTISAMPLE_15_SAMPLES;
	case MSTYPE_MULTISAMPLES_16:
		outD3DMultiSample = D3DMULTISAMPLE_16_SAMPLES;
	default:
		outD3DMultiSample = D3DMULTISAMPLE_NONE;
	}

	if( flags & USE_BEST_MULTISAMPLES )
	{
		mpDeviceDesc->GetBestMultiSample( adapterFromat, backBufferFormat, bWindowed, 
			outD3DMultiSample, outMultiSampleQuilty );
		return;
	}
	else
	{
		outMultiSampleQuilty = 0;
	}
}

bool GnDX9Renderer::RealBeginFrame()
{
	mpDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x000000, 1.0f, 0 );

	if ( FAILED( mpDevice->BeginScene() ) )
	{
#ifdef _DEBUG
		GnWarring( "GnDX9Renderer::RealBegineFrame> FAILED - probably alt-tabbed away from app" );
#endif
		return false;
	}
	return true;
}

bool GnDX9Renderer::RealEndFrame()
{
	mpDevice->EndScene(); 
	return true;
}

bool GnDX9Renderer::RealDisplayFrame()
{
	if ( FAILED( mpDevice->Present(NULL, NULL, NULL, NULL) ) )
	{
#ifdef _DEBUG
		GnWarring( "GnDX9Renderer::RealBegineFrame> FAILED - probably alt-tabbed away from app" );
#endif
		return false;
	}
	return true;
}